#ifndef M__TOOLS_Q
#define M__TOOLS_Q

#include <iostream>
#include <random>
#include <iomanip>
#include <string>
#include <sstream>
#include <cstdlib>
#include <atomic>
#include <sqlite3.h>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <cerrno>
#include "mq_log.hpp"

class UuidHelper
{
public:
    static std::string uuid()
    {
        std::random_device rd;
        std::mt19937_64 generator(rd());
        std::uniform_int_distribution<int> distribution(0, 255);
        std::stringstream ss;
        for (int i = 0; i < 8; ++i)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
            if (i == 3 || i == 5 || i == 7)
                ss << '-';
        }

        static std::atomic<size_t> seq(1);
        size_t num = seq.fetch_add(1); // atomic promise thread safe
        for (int i = 7; i >= 0; i--)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i * 8)) & 0xff);
            if (i == 6)
                ss << "-";
        }

        return ss.str();
    }
};


class SqliteHelper
{
public:
    typedef int (*SqliteCallback)(void *, int, char **, char **);

    SqliteHelper(const std::string& dbfile)
        : _dbfile(dbfile), _handler(nullptr)
    {
        EnableScreen();
    }

    bool open(int save_level = SQLITE_OPEN_FULLMUTEX)
    {
        int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | save_level, nullptr);
        if (ret != SQLITE_OK)
        {
            LOG(FATAL, "创建/打开数据库失败!!!\n, 错误消息是：%s\n", sqlite3_errmsg(_handler));
            return false;
        }
        return true;
    }

    bool exec(const std::string &sql, SqliteCallback cb, void *args)
    {
        int ret = sqlite3_exec(_handler, sql.c_str(), cb, args, nullptr);
        if (ret != SQLITE_OK)
        {
            LOG(FATAL, sql.c_str());
            LOG(FATAL, "sql语句执行失败!!!\n 错误信息:%s\n", sqlite3_errmsg(_handler));
            return false;
        }
        return true;
    }

    bool close()
    {
        if (_handler)
        {
            sqlite3_close(_handler);
            return true;
        }
        return false;
    }

private:
    std::string _dbfile;
    sqlite3 *_handler;
};

class SplitHelper
{
public:
    static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
    {
        size_t pos = 0, idx = 0;
        while (idx < str.size())
        {
            pos = str.find(sep, idx);
            // 末端
            if (pos == std::string::npos)
            {
                result.push_back(str.substr(idx));
                return result.size();
            }
            // 重复分隔符
            if (pos == idx)
            {
                idx = pos + sep.size();
                continue;
            }
            result.push_back(str.substr(idx, pos - idx));
            idx = pos + sep.size();
        }
        return result.size();
    }
};

class FileHelper
{
public:
    FileHelper(const std::string &filename)
        :_filename(filename)
    {
        EnableScreen();
    }

    bool exists()
    {
        struct stat st;
        return (::stat(_filename.c_str(), &st) == 0); // 不考虑权限不足的问题
    }

    size_t size()
    {
        struct stat st;
        int ret = ::stat(_filename.c_str(), &st);
        if(ret < 0)
        {
            LOG(FATAL,"文件不存在!!!");
        }
        return st.st_size;
    }

    bool read(char* body, size_t offset, size_t len)
    {
        // 打开文件
        std::ifstream ifs(_filename, std::ios::binary | std::ios::in); // 文本读取会导致格式“美化”
        if (ifs.is_open() == false)
        {
            LOG(FATAL, "文件未打开!!!");
            return false;
        }
        // 跳转到指定位置
        ifs.seekg(offset);
        // 读取
        ifs.read(body, len);
        if (ifs.good() == false)
        {
            LOG(FATAL, "%s 文件读取失败!!!", _filename);
            return false;
        }
        // 关闭
        ifs.close();
        return true;
    }

    // 复用
    bool read(std::string& body)
    {
        size_t size = this->size();
        body.resize(size);
        return read(&body[0], 0, size);
    }

    bool write(const char* body, size_t offset, size_t len)
    {
        // 打开文件
        std::fstream fs(_filename, std::ios::binary | std::ios::out | std::ios::in);
        if (fs.is_open() == false)
        {
            LOG(FATAL, "文件打开失败!!!");
            return false;
        }
        // 跳转到指定位置
        fs.seekp(offset);
        // 写
        fs.write(body, len);
        if (fs.good() == false)
        {
            LOG(FATAL, "%s 文件写入失败!!!", _filename);
            return false;
        }
        // 关闭
        fs.close();
        return true;
    }
    bool write(const std::string &body)
    {
        return write(body.c_str(), 0, body.size());
    }

    bool rename(const std::string &target_name)
    {
        return (::rename(_filename.c_str(), target_name.c_str()) == 0);
    }

    static std::string parentDir(const std::string &filepath)
    {
        size_t pos = filepath.find_last_of("/");
        if (pos == std::string::npos)
        {
            return "./";
        }
        std::string path = filepath.substr(0, pos);
        return path;
    }

    static bool createFile(const std::string &filename)
    {   
        std::fstream fs(filename, std::ios::out | std::ios::binary); // binary方便跨平台
        if (fs.is_open() == false)
        {
            LOG(FATAL, "文件创建失败!!!");
            return false;
        }
        fs.close();
        return true;
    }


    static bool removeFile(const std::string &filename)
    {
        return ::remove(filename.c_str()) == 0;
    }

    static bool createDirectory(const std::string &filepath)
    {   
        size_t pos = 0, idx = 0;
        while(idx < filepath.size())
        {
            pos = filepath.find("/", idx);
            if (pos == std::string::npos)
                return mkdir(filepath.c_str(), 0775);
            
            std::string path = filepath.substr(0, pos);
            int ret = mkdir(path.c_str(), 0775);
            if (ret != 0 && errno != EEXIST)
            {
                LOG(ERROR, "创建目录%s失败:%s", path.c_str(), strerror(errno));
                return false;
            }
            idx = pos + 1;
        }
        return true;
    }   

    static bool removeDirectory(const std::string &path)
    {
        std::string cmd = "rm -rf " + path;
        return system(cmd.c_str()) != -1;
    }


private:
    std::string _filename;
};

#endif